本篇博文,将整理关于单例模式(就是让一个类从始至终,只能产生一个对象,而且spring管理的类也全部是单例模式的)与多线程摩擦出的火花
1 . 懒汉模式(存在线程安全性问题)
1 |
|
- 懒汉模式—在使用的时候初始化对象
2 . 饿汉模式(简单粗暴,实现线程安全)—-静态域
1 | public class demo02 { |
- 饿汉模式—在类加载的时候初始化对象,
缺点:
1 . 如果在构造函数中有过多的其他耗时操作的话,对象的创建会很慢
2 . 而且对象创建出来了还不一定会马上使用,造成资源的浪费
使用饿汉模式相应的注意点 :
- 对象创建出来以后肯定会被使用
- 构造函数没有太多其他处理
3 . 饿汉模式(简单粗暴,实现线程安全)—-静态块
1 | public class demo05 { |
- 饿汉模式—在类加载的时候初始化对象,
缺点:
1 . 如果在构造函数中有过多的其他耗时操作的话,对象的创建会很慢
2 . 而且对象创建出来了还不一定会马上使用,造成资源的浪费
使用饿汉模式相应的注意点 :
- 对象创建出来以后肯定会被使用
- 构造函数没有太多其他处理
4. 懒汉模式—-使用synchronized实现线程安全
1 |
|
- 加上synchronized 在多线程并发访问的情况下,不再有线程安全问题,但是并不推荐,因为同一时间只有有一个线程进入此静态方法,因此效率低
5. 懒汉模式—- 双重同步锁单例模式+volatile 实现线程安全
1 | public class demo04 { |
为什么要加上volatile关键字?
这就要从CPU的指令说起, 当我们执行
1 | new demo04(); |
分下面三步走
- memory = allocate(); //分配内存空间
- ctorInstance() //初始化对象
- instance = memory // 将对象的引用指向刚分配的内存空间
在单线程的情况下是不会发生任何线程安全问题的,但是! 多线程就会受到 指令重排序的影响, JVM和CPU优化–指令重排序可能出现下面的顺序
- memory = allocate(); //分配内存空间
- instance = memory // 将对象的引用指向刚分配的内存空间,
- ctorInstance() //初始化对象
这时候双重同步锁单例模式就会出现问题,比如AB两条线程 A运行到指令3却没有真正创建对象 , 然后B去判断instance此时不为空,拿到了instance,一旦调用就会出现问题
6. 使用枚举实现单例模式 –线程安全
1 |
|
推荐使用这种方法
- 相对懒汉模式,绝对性的保证的安全问题
- 相对饿汉模式,当实例在使用的时候才开始初始化